package com.example.springboot.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.example.springboot.controller.request.BasePageRequest;
import com.example.springboot.entity.Bike;
import com.example.springboot.entity.Hire;
import com.example.springboot.entity.User;
import com.example.springboot.exception.ServiceException;
import com.example.springboot.mapper.BikeMapper;
import com.example.springboot.mapper.HireMapper;
import com.example.springboot.mapper.UserMapper;
import com.example.springboot.mapper.po.HireCountPO;
import com.example.springboot.service.HireService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * ClassName:HireServiceImpl
 * Package:com.example.springboot.service.impl
 * Description:
 *
 * @Author: Gao
 * @Create: 2023/2/7 - 16:49
 */
@Service
public class HireServiceImpl implements HireService {
    @Autowired
    HireMapper hireMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    BikeMapper bikeMapper;

    @Override
    public List<Hire> list() {

        return hireMapper.list();
    }

    @Override
    public Object page(BasePageRequest basePageRequest) {
        PageHelper.startPage(basePageRequest.getPageNum(), basePageRequest.getPageSize());
        List<Hire> hires = hireMapper.listByCondition(basePageRequest);

        for(Hire hire : hires){
            if(Objects.isNull(hire.getEnd())){
                //订单还未结束时，判断超时或者未超时
                Date start = hire.getStart();
                Date now = new Date();
                long diff = now.getTime() - start.getTime();
                if(diff > 1000 * 60 *60 ){
                    hire.setNote("已超时"+ (diff/1000/60/60 - 1) + "小时");
                } else {
                    hire.setNote("未超时");
                }
            }
        }
        return new PageInfo<>(hires);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(Hire hire) {
        //校验用户余额是否充足
        User user = userMapper.getByName(hire.getUserName());
        if(Objects.isNull(user)){
            throw new ServiceException("用户不存在");
        }
        int balance = user.getBalance();
        if (balance < 5){
            throw new ServiceException("用户余额不足");
        }
        //校验单车数量是否充足
        Bike bike = bikeMapper.getByName(hire.getBikeName());
        if(Objects.isNull(bike)){
            throw new ServiceException("单车不存在");
        }
        Integer nums = bike.getNums();
        if (nums < 1){
            throw new ServiceException("单车数量不足");
        }
        //更新单车数量,扣除用户押金
        bike.setNums(nums - 1);
        bikeMapper.updateById(bike);
        user.setBalance(user.getBalance() - 5);
        userMapper.updateById(user);

        //自动生成租借单号(20位数字，前14位为时间戳，后六位为随机数)
        String str = this.getTimestamp().replaceAll("[[\\s-:punct:]]", "");
        Random random = new Random();
        int num = random.nextInt(900000)+100000;//(int)(Math.random()*999999);
        String hireNo = str + num;

        //赋值
        hire.setHireNo(hireNo);
        hire.setNote("未超时");

        //新增租借记录
        hireMapper.save(hire);
    }

    @Override
    public void deleteById(Integer id) {
        hireMapper.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Hire hire) {
        //还车
        hire = hireMapper.getById(hire.getId());
        User user = userMapper.getByName(hire.getUserName());
        Bike bike = bikeMapper.getByName(hire.getBikeName());
        //设置结束时间
        Date date = new Date();
        hire.setEnd(date);
        if(hire.getNote() == null || hire.getNote().equals("未超时")){
            //未超时时，计算余额，向上取整 a / b + (a % b == 0 ? 0 : 1)
            Long end = date.getTime();
            Long start = hire.getStart().getTime();
            int minute = (int) (((end - start) / 60000) + ((end - start)% 60 == 0 ? 0 : 1) ); //用户花费的时间
            int money = minute / 10 + ( minute % 10 == 0? 0 : 1);   //用户应缴纳的费用
            hire.setMoney(money);
            //返还用户剩余的押金
            if (money <= 5){
                user.setBalance(user.getBalance() + (5 - money));
                hire.setNote("未超时");
            } else {
                hire.setNote("已超时");
            }
        }
        //切换状态并更新
        hire.setStatus(0);
        bike.setNums(bike.getNums() + 1);

        hireMapper.updateById(hire);
        userMapper.updateById(user);
        bikeMapper.updateById(bike);
    }

    @Override
    public Hire getById(Integer id) {
        return hireMapper.getById(id);
    }

    @Override
    public Hire getByHireNo(String hireNo) {
        return hireMapper.getByHireNo(hireNo);
    }

    @Override
    public Object getCountByTimeRange(String timeRange) {
        Map<String, Object> map = new HashMap<>();
        Date today = new Date();
        List<DateTime> dateRange;
        switch (timeRange) {
            case "week":
                // offsetDay 计算时间的一个工具方法
                // rangeToList 返回从开始时间到结束时间的一个时间范围
                dateRange = DateUtil.rangeToList(DateUtil.offsetDay(today, -6), today, DateField.DAY_OF_WEEK);
                break;
            case "month":
                dateRange = DateUtil.rangeToList(DateUtil.offsetDay(today, -29), today, DateField.DAY_OF_MONTH);
                break;
            case "month2":
                dateRange = DateUtil.rangeToList(DateUtil.offsetDay(today, -59), today, DateField.DAY_OF_MONTH);
                break;
            case "month3":
                dateRange = DateUtil.rangeToList(DateUtil.offsetDay(today, -89), today, DateField.DAY_OF_MONTH);
                break;
            default:
                dateRange = new ArrayList<>();
        }
        //  datetimeToDateStr() 就是一个处理的方法， 把 DateTime类型的List转换成一个 String的List
        List<String> dateStrRange = datetimeToDateStr(dateRange);
        map.put("date", dateStrRange);  // x轴的日期数据生产完毕

        //  timeRange = week  month
        // getCountByTimeRange 不会统计数据库没有的日期，比如 数据库 11.4 这一天数据没有，他不会返回 date=2022-11-04,count=0 这个数据
        List<HireCountPO> hireCount = hireMapper.getCountByTimeRange(timeRange, 1);// 1 hire
        map.put("hire", countList(hireCount, dateStrRange));
        return map;
    }
    // 把 DateTime类型的List转换成一个 String的List
    private List<String> datetimeToDateStr(List<DateTime> dateTimeList) {
        List<String> list = CollUtil.newArrayList();
        if (CollUtil.isEmpty(dateTimeList)) {
            return list;
        }
        for (DateTime dateTime : dateTimeList) {
            String date = DateUtil.formatDate(dateTime);
            list.add(date);
        }
        return list;
    }

    // 对数据库未统计的时间进行处理
    private List<Integer> countList(List<HireCountPO> countPOList, List<String> dateRange) {
        List<Integer> list = CollUtil.newArrayList();
        if (CollUtil.isEmpty(countPOList)) {
            return list;
        }
        for (String date : dateRange) {
            // .map(BorrowReturCountPO::getCount) 取出 对象里的 count值
            // orElse(0) 对没匹配的数据返回0
            // "2022-11-04" 没有的话 就返回0
            Integer count = countPOList.stream().filter(countPO -> date.equals(countPO.getDate()))
                    .map(HireCountPO::getCount).findFirst().orElse(0);
            list.add(count);
        }
        // 最后返回的list的元素个数会跟 dateRange 的元素个数完全一样
        // dateRange: [
        //            "2022-10-30",
        //            "2022-10-31",
        //            "2022-11-01",
        //            "2022-11-02",
        //            "2022-11-03",
        //            "2022-11-04",
        //            "2022-11-05"
        //        ],
        // borrow: [
        //            0,
        //            0,
        //            2,
        //            1,
        //            0,
        //            1,
        //            3
        //        ]
        return list;
    }

    private String getTimestamp() {
        //获取当前时间，精确到秒
        SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("时间戳："+sdfTime.format(new Date()));
        return sdfTime.format(new Date());
    }

}
